#include <vector>
#include <string>
#include <iostream>
#include "vertex.h"
#include "polygon.h"
#include "mesh.h"
#include "MeshCache.h"
#include "Singleton.h"

using namespace std;

Polygon::Polygon(const char* file_name) {
	string str = file_name;
	mesh = MeshCache::get_Instance()->getMesh(str);
}

void Polygon::draw(){
	
	glEnableClientState(GL_VERTEX_ARRAY);

	glColor3f(0, 1, 0.5);

	glVertexPointer(3, GL_FLOAT, 0, mesh->getVertices());

	glTranslatef(center.x, center.y, 0);
	glRotatef(angle, 0, 0, 1);	
	
	glDrawElements(GL_TRIANGLES, 3*(mesh->getN() - 2), GL_UNSIGNED_BYTE, mesh->getIndices());

	glRotatef(-angle, 0, 0, 1);
	glTranslatef(-center.x, -center.y, 0);

	glDisableClientState(GL_VERTEX_ARRAY);

}

void Polygon::SetCenter(Vertex v){ center = v; }

Vertex Polygon::getCenter(){ return center; }

float Polygon::getAngle(){ return angle; }

void Polygon::SetAngle(float a){ angle = a; }

void Polygon::collide(float j, Vertex n, Vertex p, Vertex cen){}

Vertex Polygon::nthVertex(int n){
	if (n >= mesh->getN())
		n -= mesh->getN();
	if (n == -1)
		n = mesh->getN() - 1;
	Vertex p = mesh->getVertices()[n];

	float x;
	x= p.x*s[0][0] + p.y*s[0][1];
	p.y = p.x*s[1][0] + p.y*s[1][1];
	p.x = x;

	p.x = p.x + center.x;
	p.y = p.y + center.y;

	return p;
}

Vertex Polygon::ClosestPoint(Vertex v){
	Vertex p1, p2, r;
	float d1, d2, d = dist(nthVertex(0), v);
	for (int i = 0; i < mesh->getN(); i++){
		p1 = nthVertex(i);
		p2 = nthVertex(i + 1);
		d1 = dot_product(diff(p1, p2), diff(p1, v));
		d2 = dot_product(diff(p2, p1), diff(p2, v));
		if ((d1 >= 0) && (d2 >= 0)){
			Line l(p1.x, p1.y, p2.x, p2.y);
			if (dist(project_point(v, l), v) <= d){
				d = dist(project_point(v, l), v);
				r = project_point(v, l);
			}
		}
		else if (dist(p1, v) < dist(p2, v)){
			if (dist(p1, v) <= d){
				d = dist(p1, v);
				r = p1;
			}
		}
		else{
			if (dist(p2, v) <= d){
				d = dist(p2, v);
				r = p2;
			}
		}
	}

		return r;
}

bool Polygon::InObject(Vertex v){
	Vertex p1, p2, p3, p4, m;
	int count = 0;
	Line l(v.x, v.y, 1.1, v.y);

	for (int i = 0; i < mesh->getN(); i++){
		p1 = nthVertex(i);
		p2 = nthVertex(i + 1);
		Line l1(p1.x, p1.y, p2.x, p2.y);

		if (parallel(l, l1)){
			if (point_on_line(v, p1, p2)){
				p3 = nthVertex(i - 1);
				p4 = nthVertex(i + 2);
				if (p3 == p4)
					continue;
				if ((p3.y - v.y)*(p4.y - v.y) < 0)
					count++;
				else
					continue;
			}
		}
		else{
			m = lines_intersection(l, l1);
			if ((m.x >= v.x) && point_on_lineseg(m, p1, p2)){
				if (m == p1)
					continue;
				if (m == p2){
					p3 = nthVertex(i + 2);
					if (p1 == p3)
						continue;
					if ((p3.y - v.y)*(p1.y - v.y) < 0)
						count++;
				}
				else 
					count++;
			}
		}
	}
	
	return (count % 2 != 0);

}

bool Polygon::IsEdgeCollision(Vertex v){
	Vertex p;
	for (int i = 0; i < mesh->getN(); i++){
		p = nthVertex(i);
		if (v == p)
			return false;
	}
	return true;
}

Vertex Polygon::GetNormal(Vertex v){
	Vertex p1, p2, n;
	for (int i = 0; i < mesh->getN(); i++){
		p1 = nthVertex(i);
		p2 = nthVertex(i + 1);
		if (point_on_lineseg(v, p1, p2)){
			Line l(p1.x, p1.y, p2.x, p2.y);			
			float m1 = sqrt(l.a*l.a + l.b*l.b);
			n.x = l.a/m1;
			n.y = l.b/m1;
			return n;
			break;
		}
	}
}
float Polygon::GetM(){ return m; }
float Polygon::GetI(){ return I; }
void Polygon::SetM(float mass){ m = mass; }
void Polygon::SetI(float f){ I = f; }

void Polygon::CalcRotMatrix(){
	float a = angle*3.1415/180;
	float cos_a = cos(a);
	float sin_a = sin(a);
	s[0][0] = cos_a;
	s[0][1] = -sin_a;
	s[1][0] = sin_a;
	s[1][1] = cos_a;
}

Vertex Polygon::GetV(){
	Vertex v;
	return v;
}

float Polygon::GetV_angle(){
	float d = 0;
	return d;
}